package com.atguigu.daijia.customer.service.impl;

import com.atguigu.daijia.common.execption.GuiguException;
import com.atguigu.daijia.common.result.ResultCodeEnum;
import com.atguigu.daijia.coupon.client.CouponFeignClient;
import com.atguigu.daijia.customer.client.CustomerInfoFeignClient;
import com.atguigu.daijia.customer.service.OrderService;
import com.atguigu.daijia.dispatch.client.NewOrderFeignClient;
import com.atguigu.daijia.driver.client.DriverInfoFeignClient;
import com.atguigu.daijia.map.client.LocationFeignClient;
import com.atguigu.daijia.map.client.MapFeignClient;
import com.atguigu.daijia.map.client.WxPayFeignClient;
import com.atguigu.daijia.model.entity.order.OrderInfo;
import com.atguigu.daijia.model.enums.OrderStatus;
import com.atguigu.daijia.model.form.coupon.UseCouponForm;
import com.atguigu.daijia.model.form.customer.ExpectOrderForm;
import com.atguigu.daijia.model.form.customer.SubmitOrderForm;
import com.atguigu.daijia.model.form.map.CalculateDrivingLineForm;
import com.atguigu.daijia.model.form.order.OrderInfoForm;
import com.atguigu.daijia.model.form.payment.CreateWxPaymentForm;
import com.atguigu.daijia.model.form.payment.PaymentInfoForm;
import com.atguigu.daijia.model.form.rules.FeeRuleRequestForm;
import com.atguigu.daijia.model.vo.base.PageVo;
import com.atguigu.daijia.model.vo.customer.ExpectOrderVo;
import com.atguigu.daijia.model.vo.dispatch.NewOrderTaskVo;
import com.atguigu.daijia.model.vo.driver.DriverInfoVo;
import com.atguigu.daijia.model.vo.map.DrivingLineVo;
import com.atguigu.daijia.model.vo.map.OrderLocationVo;
import com.atguigu.daijia.model.vo.map.OrderServiceLastLocationVo;
import com.atguigu.daijia.model.vo.order.CurrentOrderInfoVo;
import com.atguigu.daijia.model.vo.order.OrderBillVo;
import com.atguigu.daijia.model.vo.order.OrderInfoVo;
import com.atguigu.daijia.model.vo.order.OrderPayVo;
import com.atguigu.daijia.model.vo.payment.WxPrepayVo;
import com.atguigu.daijia.model.vo.rules.FeeRuleResponseVo;
import com.atguigu.daijia.order.client.OrderInfoFeignClient;
import com.atguigu.daijia.rules.client.FeeRuleFeignClient;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.Date;

@Slf4j
@Service
@SuppressWarnings({"unchecked", "rawtypes"})
@RequiredArgsConstructor
public class OrderServiceImpl implements OrderService {

    private final MapFeignClient mapFeignClient;
    private final FeeRuleFeignClient feeRuleFeignClient;
    private final OrderInfoFeignClient orderInfoFeignClient;
    private final NewOrderFeignClient newOrderFeignClient;
    private final DriverInfoFeignClient driverInfoFeignClient;
    private final LocationFeignClient locationFeignClient;
    private final CustomerInfoFeignClient customerInfoFeignClient;
    private final WxPayFeignClient wxPayFeignClient;
    private final CouponFeignClient couponFeignClient;

    /**
     * 预估订单数据
     * @param expectOrderForm
     * @return
     */
    @Override
    public ExpectOrderVo expectOrder(ExpectOrderForm expectOrderForm) {
        // 预估订单驾驶路线
        CalculateDrivingLineForm calculateDrivingLineForm = new CalculateDrivingLineForm();
        BeanUtils.copyProperties(expectOrderForm, calculateDrivingLineForm);
        DrivingLineVo drivingLineVo = mapFeignClient.calculateDrivingLine(calculateDrivingLineForm).getData();

        // 计算订单费用
        FeeRuleRequestForm feeRuleRequestForm = new FeeRuleRequestForm();
        feeRuleRequestForm.setDistance(drivingLineVo.getDistance());
        feeRuleRequestForm.setStartTime(new Date());
        feeRuleRequestForm.setWaitMinute(0);
        FeeRuleResponseVo feeRuleResponseVo = feeRuleFeignClient.calculateOrderFee(feeRuleRequestForm).getData();

        // 封装数据并返回
        ExpectOrderVo expectOrderVo = new ExpectOrderVo();
        expectOrderVo.setDrivingLineVo(drivingLineVo);
        expectOrderVo.setFeeRuleResponseVo(feeRuleResponseVo);
        return expectOrderVo;
    }

    /**
     * 乘客下单
     * @param submitOrderForm
     * @return
     */
    @Override
    public Long submitOrder(SubmitOrderForm submitOrderForm) {
        // 1 重新计算驾驶路线
        CalculateDrivingLineForm calculateDrivingLineForm = new CalculateDrivingLineForm();
        BeanUtils.copyProperties(submitOrderForm, calculateDrivingLineForm);
        DrivingLineVo drivingLineVo = mapFeignClient.calculateDrivingLine(calculateDrivingLineForm).getData();

        // 2 重新计算订单费用
        FeeRuleRequestForm feeRuleRequestForm = new FeeRuleRequestForm();
        feeRuleRequestForm.setDistance(drivingLineVo.getDistance());
        feeRuleRequestForm.setStartTime(new Date());
        feeRuleRequestForm.setWaitMinute(0);
        FeeRuleResponseVo feeRuleResponseVo = feeRuleFeignClient.calculateOrderFee(feeRuleRequestForm).getData();

        // 3 封装订单信息
        OrderInfoForm orderInfoForm = new OrderInfoForm();
        // 订单位置信息
        BeanUtils.copyProperties(submitOrderForm, orderInfoForm);
        // 订单预估里程和费用
        orderInfoForm.setExpectDistance(drivingLineVo.getDistance());
        orderInfoForm.setExpectAmount(feeRuleResponseVo.getTotalAmount());

        // 保存订单信息
        Long orderId = orderInfoFeignClient.saveOrderInfo(orderInfoForm).getData();

        // 4 添加并执行新订单定时任务，每分钟执行一次任务，查询附近可以接单的司机，给司机的临时队列中发送新订单信息，通知司机接单
        NewOrderTaskVo newOrderTaskVo = new NewOrderTaskVo();
        BeanUtils.copyProperties(orderInfoForm, newOrderTaskVo);
        newOrderTaskVo.setOrderId(orderId);
        newOrderTaskVo.setExpectTime(drivingLineVo.getDuration());
        newOrderTaskVo.setCreateTime(new Date());
        Long jobId = newOrderFeignClient.addAndStartTask(newOrderTaskVo).getData();
        log.info("开启新订单定时任务，订单 ID：{}，任务 ID：{}", orderId, jobId);

        return orderId;
    }

    /**
     * 查询订单状态
     * @param orderId
     * @return
     */
    @Override
    public Integer getOrderStatus(Long orderId) {
        return orderInfoFeignClient.getOrderStatus(orderId).getData();
    }

    /**
     * 查询乘客执行中订单基本信息
     * @param customerId
     * @return
     */
    @Override
    public CurrentOrderInfoVo searchCustomerCurrentOrder(Long customerId) {
        return orderInfoFeignClient.searchCustomerCurrentOrder(customerId).getData();
    }

    /**
     * 根据订单 ID 查询当前乘客的订单信息
     * @param orderId
     * @param customerId
     * @return
     */
    @Override
    public OrderInfoVo getOrderInfo(Long orderId, Long customerId) {
        // 获取订单信息
        OrderInfo orderInfo = orderInfoFeignClient.getOrderInfo(orderId).getData();
        if (customerId != orderInfo.getCustomerId().longValue()) {
            throw new GuiguException(ResultCodeEnum.ILLEGAL_REQUEST);
        }

        // 已有司机接单，获取接单司机信息
        DriverInfoVo driverInfoVo = null;
        if (orderInfo.getStatus() >= OrderStatus.ACCEPTED.getStatus()) {
            driverInfoVo = driverInfoFeignClient.getDriverInfo(orderInfo.getDriverId()).getData();
        }

        // 订单状态为待支付，获取订单费用信息
        OrderBillVo orderBillVo = null;
        if (orderInfo.getStatus() >= OrderStatus.UNPAID.getStatus()) {
            orderBillVo = orderInfoFeignClient.getOrderBillInfo(orderId).getData();
        }

        // 封装返回对象
        OrderInfoVo orderInfoVo = new OrderInfoVo();
        orderInfoVo.setOrderId(orderId);
        BeanUtils.copyProperties(orderInfo, orderInfoVo);
        orderInfoVo.setDriverInfoVo(driverInfoVo);
        orderInfoVo.setOrderBillVo(orderBillVo);
        return orderInfoVo;
    }

    /**
     * 根据订单 ID 查询司机基本信息
     * @param orderId
     * @param customerId
     * @return
     */
    @Override
    public DriverInfoVo getDriverInfo(Long orderId, Long customerId) {
        OrderInfo orderInfo = orderInfoFeignClient.getOrderInfo(orderId).getData();
        if (customerId != orderInfo.getCustomerId().longValue()) {
            throw new GuiguException(ResultCodeEnum.ILLEGAL_REQUEST);
        }
        return driverInfoFeignClient.getDriverInfo(orderInfo.getDriverId()).getData();
    }

    /**
     * 从缓存中获取接单司机前往代驾订单起始点过程中的位置信息
     * @param orderId
     * @return
     */
    @Override
    public OrderLocationVo getDriverLocationFromCache(Long orderId) {
        return locationFeignClient.getDriverLocationFromCache(orderId).getData();
    }

    /**
     * 计算接单司机前往代驾订单起始点的最佳路线
     * @param calculateDrivingLineForm
     * @return
     */
    @Override
    public DrivingLineVo calculateDriverRouteToOrderStartPoint(CalculateDrivingLineForm calculateDrivingLineForm) {
        return mapFeignClient.calculateDrivingLine(calculateDrivingLineForm).getData();
    }

    /**
     * 开始代驾服务后，定时获取订单服务期间司机的最新位置信息
     * @param orderId
     * @return
     */
    @Override
    public OrderServiceLastLocationVo getOrderServiceLastLocation(Long orderId) {
        return locationFeignClient.getOrderServiceLastLocation(orderId).getData();
    }

    /**
     * 分页查询乘客订单列表
     * @param customerId
     * @param page
     * @param limit
     * @return
     */
    @Override
    public PageVo findCustomerOrderPage(Long customerId, Long page, Long limit) {
        return orderInfoFeignClient.findCustomerOrderPage(customerId, page, limit).getData();
    }

    /**
     * 调用微信 JSAPI 下单接口，获取预支付交易会话标识
     * @param createWxPaymentForm
     * @return
     */
    @Override
    public WxPrepayVo createWxPayment(CreateWxPaymentForm createWxPaymentForm) {
        // 获取订单支付信息
        OrderPayVo orderPayVo = orderInfoFeignClient.getOrderPayVo(createWxPaymentForm.getOrderNo(), createWxPaymentForm.getCustomerId()).getData();
        if (OrderStatus.UNPAID.getStatus() != orderPayVo.getStatus().intValue()) {
            // 只有订单状态为待支付，才可以进行支付操作
            throw new GuiguException(ResultCodeEnum.ILLEGAL_REQUEST);
        }

        // 获取乘客微信 OpenId
        String customerOpenId = customerInfoFeignClient.getCustomerOpenId(createWxPaymentForm.getCustomerId()).getData();

        // 获取司机微信 OpenId
        String driverOpenId = driverInfoFeignClient.getDriverOpenId(orderPayVo.getDriverId()).getData();

        // 如果乘客使用了优惠券，获取减免金额
        BigDecimal couponAmount = null;
        BigDecimal payAmount = orderPayVo.getPayAmount();
        if (orderPayVo.getCouponAmount() == null && createWxPaymentForm.getCustomerCouponId() != null) {
            // orderPayVo.getCouponAmount() == null 表示之前没有使用优惠券
            // createWxPaymentForm.getCustomerCouponId() != null 表示本次使用了优惠券
            // 远程调用，使用优惠券获取减免金额
            UseCouponForm useCouponForm = new UseCouponForm();
            BeanUtils.copyProperties(createWxPaymentForm, useCouponForm);
            useCouponForm.setOrderAmount(payAmount);
            useCouponForm.setOrderId(orderPayVo.getOrderId());
            couponAmount = couponFeignClient.useCoupon(useCouponForm).getData();
        }
        // 更新订单支付金额
        if (couponAmount != null) {
            Boolean updated = orderInfoFeignClient.updateOrderCouponAmount(orderPayVo.getOrderId(), couponAmount).getData();
            if (!updated) {
                throw new GuiguException(ResultCodeEnum.UPDATE_ERROR);
            }
            // 使用了优惠券后的支付金额
            payAmount = payAmount.subtract(couponAmount);
        }

        // 封装微信支付所需参数
        PaymentInfoForm paymentInfoForm = new PaymentInfoForm();
        BeanUtils.copyProperties(orderPayVo, paymentInfoForm);
        paymentInfoForm.setCustomerOpenId(customerOpenId);
        paymentInfoForm.setDriverOpenId(driverOpenId);
        // 支付方式：1-微信
        paymentInfoForm.setPayWay(1);
        // 使用了优惠券后的支付金额
        paymentInfoForm.setAmount(payAmount);

        return wxPayFeignClient.createWxPayment(paymentInfoForm).getData();
    }

    /**
     * 查询订单支付状态
     * @param orderNo
     * @return
     */
    @Override
    public Boolean queryPayStatus(String orderNo) {
        return wxPayFeignClient.queryPayStatus(orderNo).getData();
    }
}
